总结 Sass 的核心用法
选择器嵌套与 & 标识符
// 节点
.wrap {
border: black 1px solid;
// 节点
& > ul:last-child {
border: black 1px solid;
}
// 节点(无样式属性)
div:first-child {
// 节点
&.intro > p {
color: skyblue;
// 节点
&:hover {
border: black 1px solid;
}
}
// 节点
&:hover {
background: green;
}
}
}
Tip:
- 为了方便描述,我们把整个
scss
内的样式结构看作是一个森林,最外层的选择器就是每棵树的根节点,内部的每一层选择器就是一个节点。
现在我们需要知道如下几个要点:
- 把
scss
中的每个选择器当作一个节点 - 整个编译的过程如下:
- 以每棵树的根节点为单位,用一个指针指向树的根节点
- 以前序遍历的方式(根左右)遍历每个节点
- 遇到内部 有样式属性的节点 则停下,记录一下从根节点到当前节点到路径,这个路径实际上就是编译为
css
的结果- 需要注意的是,指针停下来时,如果当前节点中含有
&
标识符,那么&
标识符会被替换为路径中的上一个节点 - 如果不含
&
标识符,则会和上一个节点构成 后代选择器
- 需要注意的是,指针停下来时,如果当前节点中含有
下面我们一步步来分析上面的 scss
代码如何编译为 css
代码。
最开头的 scss
样式的树结构如下:
根据上面的要点,我们就可以很容易的写出如下 scss
编译后的内容:
.wrap {}
.wrap > ul:last-child {}
.wrap div:first-child.intro > p {}
.wrap div:first-child.intro > p:hover {}
.wrap div:first-child:hover {}
注释
scss
中有两种注释:
/* 注释 */
,最终会被编译到 css 文件中// 注释
,不会被编译到 css 文件中
变量
语法:$key: value
,其中 key
为变量名,value
为变量值。
$width: 5em;
#main {
width: $width;
}
并且变量还能声明在选择器内部,我们把这种变量称为局部变量:
#main {
$width: 5em;
width: $width;
}
#second {
width: $width; // 错误的,因为无法获取到 #main 选择器内部的局部变量
}
给变量添加 !global
声明可以将该变量转为全局变量,当然只局限于该 .scss
文件中。
#main {
$width: 5em !global;
width: $width;
}
#second {
width: $width; // 可以获取到 $width 变量
}
在给变量赋值时添加 !default
可以防止此次赋值覆盖掉之前取值非 null 的赋值。
$content: "First content";
$content: "Second content?" !default;
#main {
content: $content;
}
编译为:
#main {
content: "First content";
}
插槽
语法:#{}
,一般用于在 选择器、属性名中插入变量。
$name: foo;
$attr: border;
p.#{$name} {
#{$attr}-color: blue;
}
数据类型及其常用函数
概览
scss
中支持 6 种数据类型值:
- 数字:
1, 2, 2em, 3px, 10%...
- 字符串:
'foo', "bar", baz
- 颜色:
blue, #ffffff, rgba(255,0,0,0.5)
- 布尔类型:
true, false
- 空值:
null
- 数组:用空格或者逗号分隔,建议用逗号分隔
2px, 3px, 2em, 20%
- 键值对:
(key1: value1, key2: value2, key3: value3)
SassScript 也支持其他 CSS 属性值,比如 Unicode 字符集,或 !important 声明。然而Sass 不会特殊对待这些属性值,一律视为无引号字符串。
字符串
分为两类:
- 有引号字符串,可以是单引号或双引号,比如:
"Lucida Grande"
、'http://sass-lang.com'
- 无引号字符串,比如:
sans-serif
、bold
使用
#{}
插入的字符串最终都会被编译为无符号字符串。
数组
语法
- 以逗号分隔:
$colors: (red, blue, skyblue, green)
,建议用这个 - 以空格分隔:
$colors: (red blue skyblue green)
基本用法
$colors: (blue, green, skyblue, red);
@each $color in $colors {
.#{$color}-color {
color: $color;
}
}
编译为:
.blue-color {
color: blue;
}
.green-color {
color: green;
}
.skyblue-color {
color: skyblue;
}
.red-color {
color: red;
}
二维数组
$borders: ((1px solid black), (1px dot green));
@each $border in $borders {
.#{nth($border, 3)}-#{nth($border, 3)}-border {
border: $border;
}
}
编译为:
.solid-black-border {
border: 1px solid black;
}
.dot-green-border {
border: 1px dotted green;
}
Sass 中有关处理数组的常用内置函数
nth($list, index)
获取数组指定下标的值。
length($list)
获取指定数组长度。
append($list, $val, $separator: auto)
向数组中添加元素,$separator
可以缺省。
join($list1, $list2, $separator: auto, $bracketed: auto)
合并两个数组,$separator
、$bracketed
可以缺省。
index($list, $value)
获取数组指定值的下标。
另外还有一些处理数组元素分隔符的函数,具体可以参考 官方文档
注意:
- Sass 中的数组下标是从 1 开始的。
$separator
的取值可以是 comma(逗号), space(空格), or slash(短斜线)
键值对
语法
$map: (
"name": kll,
"gender": boy
)
注意这里的 key 是字符串类型,可以带引号也可以不带。
遍历
@each
$radius-sizes: (
small-radius: 5px,
normal-radius: 10px,
large-radius: 15px
);
@each $key, $value in $radius-sizes {
.#{$key} {
border-radius: $value;
}
}
@for
$radius-sizes: (
small-radius: 5px,
normal-radius: 10px,
large-radius: 15px
);
@for $i from 1 through length($radius-sizes) {
$keys: map-keys($radius-sizes);
$cur-key: nth($keys, $i);
.#{$cur-key} {
border-radius: map-get($radius-sizes, $cur-key);
}
}
map-keys
,传入一个键值对,获取该键值对的键值列表map-get
,第一个参数为键值对,后续传入一个 key,获取 valuenth
,不仅可以计算列表长度,也可以计算键值对长度
常用函数
以下函数使用前需要引入:
@use "sass:map";
map.get 与 map.set
map.get
等价于 map-get
,前者需要 @use "sass:map"
,后者则不需要。
$font-weights: ("regular": 400, "medium": 500, "bold": 700);
@debug map.get($font-weights, "medium"); // 500
@debug map.get($font-weights, "extra-bold"); // null
$font-weights: ("regular": 400, "medium": 500, "bold": 700);
@debug map.set($font-weights, "regular", 300);
// ("regular": 300, "medium": 500, "bold": 700)
map.keys 与 map.values
这两个都可以写成 map-keys
或 map-values
。
$font-weights: ("regular": 400, "medium": 500, "bold": 700);
@debug map.keys($font-weights); // "regular", "medium", "bold"
$font-weights: ("regular": 400, "medium": 500, "bold": 700);
@debug map.values($font-weights); // 400, 500, 700
map.has-key
$font-weights: ("regular": 400, "medium": 500, "bold": 700);
@debug map.has-key($font-weights, "regular"); // true
@debug map.has-key($font-weights, "bolder"); // false
map.merge 与 map.deep-merge
$helvetica-light: (
"weights": (
"lightest": 100,
"light": 300
)
);
$helvetica-heavy: (
"weights": (
"medium": 500,
"bold": 700
)
);
@debug map.deep-merge($helvetica-light, $helvetica-heavy);
// (
// "weights": (
// "lightest": 100,
// "light": 300,
// "medium": 500,
// "bold": 700
// )
// )
@debug map.merge($helvetica-light, $helvetica-heavy);
// (
// "weights": (
// "medium: 500,
// "bold": 700
// )
// )
map.remove 与 map.deep-remove
$fonts: (
"Helvetica": (
"weights": (
"regular": 400,
"medium": 500,
"bold": 700
)
)
);
@debug map.deep-remove($fonts, "Helvetica", "weights", "regular");
// (
// "Helvetica": (
// "weights: (
// "medium": 500,
// "bold": 700
// )
// )
// )
运算
所有的数据类型都支持 ==
和 !=
的运算。
数字类型运算
Sass 中支持 +, -, *, /, %
的数字运算,部分不同单位取值可以混合运算(没有明确)。
加法
可以省略加数的单位,默认和被加数一致:
$width: 100% + 10;
.container {
width: $width; // 110%
}
减法同理。
除法
/
在 CSS 中通常起到分隔数字的用途,SassScript 作为 CSS 语言的拓展当然也支持这个功能,同时也赋予了 /
除法运算的功能。
以下三种情况 /
被视为除法运算符号:
- 被圆括号包裹
- 对变量、函数返回值进行除法运算
- 结合其他运算符的算术表达式
$width: 1000px;
p {
font-size: 8px / 2; // 无效,并不等于 4px
font-size: (8px / 2); // 4px
width: $width / 2; // 500px
width: round(1.5px) / 2; // 1px
height: 5px + 8px / 2px; // 9px
}
如果不希望 /
对变量做除法,可以使用 #{}
插值将变量包裹起来:
p {
$font-size: 12px;
$line-height: 30px;
font: #{$font-size}/#{$line-height};
}
注意: 对于乘法、除法运算,单位之间也会进行运算
- 比如
2px * 2px
并不是我们想要的4px
,而是4px^2
,正确的应该是2px * 2
(2px / 2px)
结果是1
却不是1px
,正确应该是2px / 2
字符串类型运算
+
可以用于拼接字符串:
p {
cursor: e + -resize;
}
编译为:
p {
cursor: e-resize;
}
注意,如果有引号字符串(位于 + 左侧)连接无引号字符串,运算结果是有引号的,相反,无引号字符串(位于 + 左侧)连接有引号字符串,运算结果则没有引号。
p:before {
content: "Foo " + Bar;
font-family: sans- + "serif";
}
编译为:
p:before {
content: "Foo Bar";
font-family: sans-serif;
}
运算表达式与其他值连用时,用空格做连接符:
p {
margin: 3px + 4px auto;
}
编译为:
p {
margin: 7px auto;
}
逻辑运算符
and
、or
、not
对应与或非。
内置函数
概览
Sass 中内置了如下的函数,可以处理相关的数据:
sass:color
sass:list
sass:map
sass:math
sass:meta
sass:selector
sass:string
在数组数据类型介绍中,介绍了它的内置的函数,这里主要来介绍有关 map、color 和 math 相关的内置函数。
通用的内置函数
url()
传入一个 url,可以是相对路径和绝对路径,用于解析目标资源。
if($condition, $if-true, $if-false)
第一个参数添布尔类型表达式,结果是 true 则返回第二个参数值反之返回第三个参数值。
$a: 10px;
$b: 20px;
.container {
font-size: if($a > $b, $a, $b);
}
颜色相关
详见之前写的 blog:CSS颜色与Sass中的常用颜色函数
数学相关
占位 ... 待补充
继承
声明一个基类。
%base {
font-size: 16px;
width: 20px;
height: 20px;
}
我们就可以在其他选择器里面通过 @extends 继承里面的样式。
.content {
@extend %base; // 里面包含了 %base 的样式
}
@mixin
基本用法
在 mixin 内部可以定义一个样式片段,这个样式片段可以包含 Sass 中的所有特性:
@mixin large-text {
font: {
family: Arial;
size: 20px;
weight: bold;
}
color: #ff0000;
}
我们可以在需要使用这个样式片段的地方通过 @include
引入 mixin:
.container {
@include large-text;
padding: 4px;
margin-top: 10px;
}
当然也可以在全局作用域中引入 mixin:
@mixin silly-links {
a {
color: blue;
background-color: red;
}
}
@include silly-links;
也可以在 mixin 中引入其他 mixin:
@mixin compound {
@include highlighted-background;
@include header-text;
}
@mixin highlighted-background { background-color: #fc0; }
@mixin header-text { font-size: 20px; }
并且 mixin 的引入和它定义的先后无关,可以类比于 JS 中的 function。
参数传递
mixin 可以像 JS 的函数那样,传递参数:
@mixin sexy-border($color, $width) {
border: {
color: $color;
width: $width;
style: dashed;
}
}
p { @include sexy-border(blue, 1in); }
传递参数的时为了提高可读性,可以加上参数名:
p { @include sexy-border($color: blue, $width: 1in); }
并且,参数还可以设置默认值,如果 @include
mixin 时,该参数没有传入,则会使用默认值:
@mixin sexy-border($color, $width: 1in) {
border: {
color: $color;
width: $width;
style: dashed;
}
}
p { @include sexy-border(blue); }
h1 { @include sexy-border(blue, 2in); }
有时传递的参数并不能确定其个数,我们在定义 mixin 时可以这样做:
@mixin box-shadow($shadows...) {
-moz-box-shadow: $shadows;
-webkit-box-shadow: $shadows;
box-shadow: $shadows;
}
.shadows {
@include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}
对于 $shadows
就成了一个数组。
当然,通过 @include
调用 mixin 时,我们也可以通过扩展运算符来展开一个数组参数:
@mixin colors($text, $background, $border) {
color: $text;
background-color: $background;
border-color: $border;
}
$values: #ff0000, #00ff00, #0000ff;
.primary {
@include colors($values...);
}
@function
@function
的用法跟 @mixin
差不多,但是 @function
多了一个 @return
,它可以更明确的确定要返回什么内容,@mixin
多用于返回样式片段。
语法:
// 定义函数
@function fn($param) {
// 函数体 ...
// 返回值
@return returnValue;
}
// 函数调用
.container {
width: fn(5); // 也可以这样:fn($param: 5)
}
@for
语法:@for $var from <start> through <end>
,其中 $var
必须是一个变量,<start>
和 <end>
必须是整数。
@for $i from 1 through 3 {
.item-#{$i} { width: 2em * $i; }
}
最终会输出每次循环的内容,如下:
.item-1 { width: 2em; }
.item-2 { width: 4em; }
.item-3 { width: 6em; }
@each
语法:$var in <list>
,其中 $var
必须是一个变量,表示每次循环时 <list>
中的元素,<list>
是列表,比如 (puma, sea-slug, egret, salamander)
就代表一个列表。
@each $animal in (puma, sea-slug, egret, salamander) {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
}
}
当然你也可以一次性声明多个变量对应多个列表:
@each $animal, $color, $cursor in (puma, black, default), (sea-slug, blue, pointer), (egret, white, move) {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
border: 2px solid $color;
cursor: $cursor;
}
}
只会循环输出 3 次,因为列表中的元素个数为 3 个,最终输出:
.puma-icon {
background-image: url('/images/puma.png');
border: 2px solid black;
cursor: default;
}
.sea-slug-icon {
background-image: url('/images/sea-slug.png');
border: 2px solid blue;
cursor: pointer;
}
.egret-icon {
background-image: url('/images/egret.png');
border: 2px solid white;
cursor: move;
}
另外,还可以遍历键值对,比如 (h1: 2em, h2: 1.5em, h3: 1.2em)
,里面每一个元素是 key: value
结构的,我们可以直接定义两个变量来遍历输出它:
@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {
#{$header} {
font-size: $size;
}
}
@if
@if
表达式返回的不是 null 或者 false 时,条件成立,输出 {}
里面的内容
$blue-base: #1890ff;
@for $i from 1 through 10 {
// 1 ~ 6
@if $i <= 6 {
.blue-#{$i} {
background: tint($blue-base, (6 - $i) * 10);
}
}
// 6 ~ 10
@else {
.blue-#{$i} {
background: shade($blue-base, ($i - 6) * 10);
}
}
}